import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.io.InputStream;
import java.io.DataInputStream;
import java.io.OutputStream;
import java.io.DataOutputStream;
import java.io.File;
import javax.imageio.ImageIO;

import lejos.pc.comm.*;
import lejos.nxt.*;

public class OrangeInterface extends Frame {
	private HashMap<String,Panel> panels;
	private HashMap<String,BufferedImage> resources;
	private HashMap<String,TexturePaint> patterns;
	private KListener kl;
	DataInputStream dis;
	DisWrap gdisw;
	Data data;
	

	OrangeInterface() {
		panels = new HashMap<String,Panel>();
		resources = new HashMap<String,BufferedImage>();
		patterns = new HashMap<String,TexturePaint>();
		data = new Data();
		gdisw = new DisWrap();
		WindowListener wl = new WListener();
		kl = new KListener(data,gdisw);
		addWindowListener(wl);
		addKeyListener(kl);
	}
	
	public static void main(String[] args) {
		GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
		GraphicsDevice d = env.getDefaultScreenDevice();
		OrangeInterface f = new OrangeInterface();
		f.setTitle("Hardcore Interface");
		
		//hide cursor
			int[] pixels = new int[16 * 16];
			Image image = Toolkit.getDefaultToolkit().createImage(new MemoryImageSource(16, 16, pixels, 0, 16));
			Cursor transparentCursor = Toolkit.getDefaultToolkit().createCustomCursor(image, new Point(0, 0), "invisibleCursor");
			f.setCursor(transparentCursor);
		
		f.setVisible(true);
		d.setFullScreenWindow((Window)f);
		f.createBufferStrategy(2);
		
		//threads
		LoadScreen ls = new LoadScreen(f.getBufferStrategy());
		new Thread(ls).start();
		f.loadResources(ls);
		BTCommunicator c = new BTCommunicator(f.panels.get("bluetooth"),f.resources,f.patterns,f.gdisw);
		Thread bt_thread = new Thread(c);
		bt_thread.start();
		ls.signalReady();
		SidePanels sp = new SidePanels(f.panels,f.resources,f.patterns, f.data);
		GraphicsClass gc = new GraphicsClass(f.getBufferStrategy(),f.panels,f.resources,f.patterns,sp);
		Thread gct = new Thread(gc);
		gct.start();
		f.kl.addThread((MyRunnable)gc);
		LightChart lc = new LightChart(f.panels.get("light_chart"),f.resources,f.data);
		Thread lct = new Thread(lc);
		f.kl.addThread((MyRunnable)lc);
		Map m = new Map(f.panels.get("LM"),f.resources);
		Center center = new Center(f.panels.get("MID"),f.resources);
		Panic p = new Panic(f.panels.get("panic"),f.resources,f.data);
		try {bt_thread.join();}
		catch(Exception e){;}
		lct.start();
		f.dis = f.gdisw.getDis();
		/*Thread spt = new Thread(sp);
		spt.start();
		f.kl.addThread((MyRunnable)sp);*/
		//now start reading values
		while(f.dis!=null && f.data.run) {
			try {
				switch(f.dis.readByte()) {
					case 0:
						f.data.leftMotor = -f.dis.readInt();
						break;
					case 1:
						f.data.rightMotor = -f.dis.readInt();
						break;
					case 2:
						f.data.light = f.dis.readInt();
						break;
					case 3:
						f.data.color = f.dis.readByte();
						break;
					case 4:
						int temp = f.dis.readInt();
						if(!(temp==255)) f.data.sonar = temp;
						break;
					case 5:
						f.data.barcode = f.dis.readByte();
						break;
					case 6:
						for(int i = 0; i<36; i++) {
							f.data.panicData[i] = f.dis.readInt();
						}
						p.show();
						break;
					case 7:
						f.data.paneel = f.dis.readByte();
						center.update(f.data.paneel);
						m.update(f.data.paneel);
				}
				Thread.sleep(50);
			} catch(Exception e) {
				System.out.println(e.getMessage());
			}
		}
	}
	
	public void loadResources(LoadScreen ls) {
		//scanlines
		ls.setMsg("Drawing Scanlines");
		BufferedImage t = new BufferedImage(1,5,BufferedImage.TYPE_INT_RGB);
		Graphics2D g2d = (Graphics2D)t.getGraphics();
		g2d.setColor(new Color(16,16,16));
		g2d.fillRect(0,0,1,3);
		g2d.setColor(new Color(32,32,32));
		g2d.fillRect(0,3,1,2);
		TexturePaint tpat = new TexturePaint(t,new Rectangle(1,5));
		patterns.put("main_bg",tpat);
		g2d.dispose();
		
		//blur
		ls.setMsg("Preparing Blur");
		/*int blursize = 150;
		t = new BufferedImage(1920+blursize*2,1080+blursize*2,BufferedImage.TYPE_INT_ARGB);
		BufferedImage overlay = new BufferedImage(1920+blursize*2,1080+blursize*2,BufferedImage.TYPE_INT_ARGB);
		g2d = (Graphics2D)overlay.getGraphics();
		g2d.setBackground(new Color(0,0,0,0));
		g2d.clearRect(250,250,1920,1080);
		g2d.setColor(new Color(0,0,0,160));
		g2d.fillRect(0,0,1920+2*blursize,50+blursize);
		g2d.fillRect(0,1080+blursize-50,1920+2*blursize,50+blursize);
		g2d.fillRect(0,blursize+50,50+blursize,1080-2*50);
		g2d.fillRect(1920+blursize-50,blursize+50,50+blursize,1080-2*50);
		g2d.setColor(new Color(255,255,255,110));
		g2d.fillRect(blursize+200,blursize+200,1920-600,50);
		g2d.dispose();
		float[] data = new float[blursize*2+1];
		float total = -(float)(blursize);
		for(int x=0;x<blursize+1;x++) {
			data[x] = (float)x;
			data[2*blursize-x] = (float)x;
			total += 2*(float)x;
		}
		for(int x=0;x<2*blursize+1;x++) {
			data[x] /= total;
		}
		Kernel k = new Kernel(2*blursize+1,1,data);
		ConvolveOp op = new ConvolveOp(k,ConvolveOp.EDGE_NO_OP,null);
		op.filter(overlay,t);
		k = new Kernel(1,2*blursize+1,data);
		op = new ConvolveOp(k,ConvolveOp.EDGE_NO_OP,null);
		ls.setMsg("Blurring");
		op.filter(t,overlay);*/
		t = new BufferedImage(16,9,BufferedImage.TYPE_INT_ARGB);
		g2d = (Graphics2D)t.getGraphics();
		g2d.setBackground(new Color(0,0,0,15));
		g2d.clearRect(0,0,16,9);
		g2d.setBackground(new Color(0,0,0,0));
		g2d.clearRect(1,1,14,7);
		g2d.setBackground(new Color(255,255,255,20));
		g2d.clearRect(2,1,12,1);
		g2d.dispose();
		BufferedImage overlay = new BufferedImage(1920,1080,BufferedImage.TYPE_INT_ARGB);
		g2d = (Graphics2D)overlay.getGraphics();
		g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BICUBIC);
		g2d.drawImage(t, new AffineTransform(120f,0f,0f,120f,0,0), null);
		g2d.dispose();
		resources.put("overlay",overlay);
		
		
		
		//BTStatus Window
		ls.setMsg("Preparing Bluetooth Status Window");
		//pattern
		t = new BufferedImage(1,1,BufferedImage.TYPE_INT_ARGB);
		g2d = (Graphics2D)t.getGraphics();
		g2d.setColor(new Color(255,0,0,96));
		g2d.fillRect(0,0,1,1);
		//g2d.setColor(new Color(96,0,0));
		//g2d.fillRect(0,3,1,2);
		TexturePaint bt_pat = new TexturePaint(t,new Rectangle(1,5));
		patterns.put("bt_bg",bt_pat);
		g2d.dispose();
		//background
		BufferedImage BTstatus_bg = new BufferedImage(601,401,BufferedImage.TYPE_INT_ARGB);
		g2d = (Graphics2D)BTstatus_bg.getGraphics();
		g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
		int[] xs = {50,600,600,550,0,0};
		int[] ys = {0,0,350,400,400,50};
		Polygon p = new Polygon(xs,ys,6);
		g2d.setPaint(bt_pat);
		g2d.fill(p);
		g2d.setColor(new Color(255,230,230,64));
		g2d.setFont(new Font("Monospaced",Font.BOLD,500));
		g2d.drawString("!",150,350);
		g2d.setColor(new Color(255,230,230));
		g2d.draw(p);
		g2d.drawLine(0,50,600,50);
		g2d.setFont(new Font("Monospaced",Font.BOLD,30));
		g2d.drawString("Bluetooth Status",60,40);
		g2d.dispose();
		resources.put("bt_bg",BTstatus_bg);
		//window
		
		//Panic Window
		ls.setMsg("Preparing Panic Window");
		BufferedImage Panic_bg = new BufferedImage(601,601,BufferedImage.TYPE_INT_ARGB);
		g2d = (Graphics2D)Panic_bg.getGraphics();
		g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
		int[] xs3 = {50,600,600,550,0,0};
		int[] ys3 = {0,0,550,600,600,50};
		p = new Polygon(xs3,ys3,6);
		g2d.setPaint(bt_pat);
		g2d.fill(p);
		g2d.setColor(new Color(255,230,230));
		g2d.draw(p);
		g2d.drawLine(0,50,600,50);
		g2d.setFont(new Font("Monospaced",Font.BOLD,30));
		g2d.drawString("Panic!",60,40);
		g2d.dispose();
		resources.put("panic_bg",Panic_bg);
		//window
		
		
		//side windows
		ls.setMsg("Preparing Status Panels");
		//pattern
		t = new BufferedImage(1,1,BufferedImage.TYPE_INT_ARGB);
		g2d = (Graphics2D)t.getGraphics();
		g2d.setColor(new Color(255,128,0,32));
		g2d.fillRect(0,0,1,1);
		//g2d.setColor(new Color(96,0,0));
		//g2d.fillRect(0,3,1,2);
		TexturePaint side_pat = new TexturePaint(t,new Rectangle(1,5));
		patterns.put("side_bg",side_pat);
		g2d.dispose();
		//background
		BufferedImage side_bg = new BufferedImage(531,341,BufferedImage.TYPE_INT_ARGB);
		g2d = (Graphics2D)side_bg.getGraphics();
		g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
		int[] xs2 = {50,530,530,480,0,0};
		int[] ys2 = {0,0,290,340,340,50};
		p = new Polygon(xs2,ys2,6);
		g2d.setPaint(side_pat);
		g2d.fill(p);
		g2d.setColor(new Color(255,128,0));
		g2d.draw(p);
		g2d.drawLine(0,50,530,50);
		g2d.dispose();
		resources.put("side_bg",side_bg);
		//windows
		ls.setMsg("Creating Panels");
		Panel LT = new Panel(10,10,530,340);
		panels.put("LT",LT);
		Panel LM = new Panel(10,370,530,700);
		panels.put("LM",LM);
		Panel MID = new Panel(695,150,530,700);
		panels.put("MID",MID);
		//Panel LB = new Panel(10,730,530,340);
		//panels.put("LB",LB);
		Panel RT = new Panel(1380,10,530,340);
		panels.put("RT",RT);
		Panel RM = new Panel(1380,370,530,340);
		panels.put("RM",RM);
		Panel RB = new Panel(1380,730,530,340);
		panels.put("RB",RB);
		Panel BTstatus = new Panel(660,340,600,400);
		panels.put("bluetooth",BTstatus);
		Panel Panic = new Panel(660,240,600,600);
		panels.put("panic",Panic);
		
		
		//lightreader icon
		ls.setMsg("Lightsensor Icon");
		t = new BufferedImage(120,120,BufferedImage.TYPE_INT_ARGB);
		g2d = (Graphics2D)t.getGraphics();
		g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
		g2d.setStroke(new BasicStroke(2f));
		g2d.setColor(new Color(255,128,0));
		g2d.draw(new Rectangle(25,40,60,30));
		g2d.draw(new Rectangle(35,70,30,10));
		g2d.drawLine(70,70,70,40);
		g2d.drawLine(90,50,115,30);
		g2d.drawLine(90,60,115,80);
		g2d.setBackground(new Color(0,0,0,0));
		g2d.clearRect(35,35,40,20);
		g2d.draw(new Rectangle(35,35,40,20));
		g2d.drawOval(37,71,6,6);
		g2d.drawOval(47,71,6,6);
		g2d.drawOval(57,71,6,6);
		g2d.drawLine(25,50,20,40);
		g2d.draw(new CubicCurve2D.Double(25,50,-15,35,35,80,20,103));
		g2d.dispose();
		BufferedImage t2 = new BufferedImage(170,170,BufferedImage.TYPE_INT_ARGB);
		g2d = (Graphics2D)t2.getGraphics();
		g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
		g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BICUBIC);
		AffineTransform at = new AffineTransform();
		at.rotate(Math.PI/4d,60,60);
		g2d.drawImage(t, at, null);
		g2d.dispose();
		resources.put("icon_light",t2);
		
		//lightreader chart
		panels.put("light_chart",new Panel(1540,100,350,150));
		g2d = (Graphics2D)panels.get("light_chart").getGraphics();
		g2d.setColor(new Color(255,128,0));
		g2d.setBackground(new Color(0,0,0,255));
		g2d.clearRect(0,0,350,150);
		g2d.dispose();
		
		//track icons
		BufferedImage t_straight = new BufferedImage(50,50,BufferedImage.TYPE_INT_ARGB);
		g2d = (Graphics2D)t_straight.getGraphics();
		g2d.setStroke(new BasicStroke(4f));
		g2d.setBackground(new Color(255,128,0,96));
		g2d.clearRect(0,0,50,50);
		g2d.setColor(new Color(255,128,0));
		g2d.drawLine(1,1,1,49);
		g2d.drawLine(49,1,49,49);
		g2d.dispose();
		resources.put("straight",t_straight);
		
		BufferedImage t_left = new BufferedImage(50,50,BufferedImage.TYPE_INT_ARGB);
		g2d = (Graphics2D)t_left.getGraphics();
		g2d.setStroke(new BasicStroke(4f));
		g2d.setBackground(new Color(255,128,0,96));
		g2d.clearRect(0,0,50,50);
		g2d.setColor(new Color(255,128,0));
		g2d.drawLine(1,1,49,1);
		g2d.drawLine(49,1,49,49);
		g2d.drawLine(1,49,1,49);
		g2d.dispose();
		resources.put("left",t_left);
		
		//same as right actually but it's easier for the transform values		
		BufferedImage t_right = new BufferedImage(50,50,BufferedImage.TYPE_INT_ARGB);
		g2d = (Graphics2D)t_right.getGraphics();
		g2d.setStroke(new BasicStroke(4f));
		g2d.setBackground(new Color(255,128,0,96));
		g2d.clearRect(0,0,50,50);
		g2d.setColor(new Color(255,128,0));
		g2d.drawLine(1,1,1,49);
		g2d.drawLine(1,1,49,1);
		g2d.drawLine(49,49,49,49);
		g2d.dispose();
		resources.put("right",t_right);
	}
}

class WListener extends WindowAdapter {
	public void windowClosing(WindowEvent e) {
		e.getWindow().dispose();
	}
}

class KListener extends KeyAdapter {
	private ArrayList<MyRunnable> threads;
	private DisWrap disw;
	Data d;
	
	KListener(Data d, DisWrap disw) {
		threads = new ArrayList<MyRunnable>();
		this.d = d;
		this.disw = disw;
	}
	public void addThread(MyRunnable t) {
		threads.add(t);
	}
	
	public void keyPressed(KeyEvent e) {
		switch(e.getKeyCode()) {
			case KeyEvent.VK_ESCAPE:
				d.run = false;
				((Window)e.getSource()).dispose();
				for(MyRunnable t : threads) {
					t.kill();
				}
				break;
			case KeyEvent.VK_SPACE:
				d.takeShot = true;
				break;
			case KeyEvent.VK_ENTER:
				try {
					disw.getDos().writeByte((byte)1);
				} catch(Exception ioe) {
					;
				}
				break;
		}
	}
}

class LoadScreen implements Runnable {
	private BufferStrategy bf;
	private boolean stop;
	private String msg;
	public LoadScreen(BufferStrategy bf) {
		this.bf = bf;
		stop=false;
		msg="Loading...";
	}
	public void signalReady() {
		stop = true;
	}
	public void setMsg(String msg) {
		this.msg = msg;
	}
	public void run() {
		while(!stop) {
			Graphics2D g2d = (Graphics2D)bf.getDrawGraphics();
			g2d.setBackground(new Color(0,0,0));
			g2d.clearRect(0,0,1920,1080);
			g2d = (Graphics2D)bf.getDrawGraphics();
			g2d.setColor(new Color(255,128,0));
			g2d.drawString(msg,800,500);
			g2d.dispose();
			bf.show();
		}
	}
}

class BTCommunicator implements Runnable {
	Panel panel;
	HashMap<String,BufferedImage> r;
	HashMap<String,TexturePaint> p;
	DisWrap disw;
	public BTCommunicator(Panel panel,HashMap<String,BufferedImage> r,HashMap<String,TexturePaint> p,DisWrap disw) {
		this.panel = panel;
		this.r = r;
		this.p = p;
		this.disw = disw;
	}
	public void run() {
		Graphics2D g2d = this.preparePanel();
		try {
			g2d.drawString("Connecting...",15,85);
			NXTComm nxtComm = NXTCommFactory.createNXTComm(NXTCommFactory.BLUETOOTH);
			NXTInfo nxtInfo = new NXTInfo(NXTCommFactory.BLUETOOTH,"Jos", "00:16:53:07:A6:2D");
			nxtComm.open(nxtInfo);
			OutputStream os = nxtComm.getOutputStream();
			disw.setDos(new DataOutputStream(os));
			InputStream is = nxtComm.getInputStream();
			disw.setDis(new DataInputStream(is));
			g2d.drawString("Connected!",15,105);
			this.hidePanel();
		} catch(NXTCommException e) {
			g2d.drawString("Error establishing connection:",15,105);
			this.drawStr(g2d,e.getMessage(),15,125,48);
		}
		g2d.dispose();
	}
	private int drawStr(Graphics2D g2d, String s, int x_off, int y_off,int char_w) {
		int cnt = 0;
		String[] arr = new String[s.length()/char_w + ((s.length()%char_w!=0)?1:0)];
		while(s.length()>char_w) {
			arr[cnt] = s.substring(0,char_w);
			s = s.substring(char_w);
			cnt++;
		}
		arr[cnt] = s;
		
		cnt = 0;
		for(String str : arr) {
			g2d.drawString(str,x_off,y_off+20*cnt);
			cnt++;
		}
		return 20*cnt;
	}
	private Graphics2D preparePanel() {
		Graphics2D g2d = (Graphics2D)panel.getGraphics();
		g2d.setBackground(new Color(0,0,0,0));
		g2d.clearRect(0,0,panel.w+1,panel.h+1);
		g2d.drawImage(r.get("bt_bg"), new AffineTransform(), null);
		g2d.setColor(new Color(255,230,210));
		g2d.setFont(new Font("Monospaced",0,20));
		return g2d;
	}
	
	private void hidePanel() {
		Graphics2D g2d = (Graphics2D)panel.getGraphics();
		g2d.setBackground(new Color(0,0,0,0));
		g2d.clearRect(0,0,601,401);
		g2d.dispose();
	}
}

class DisWrap {
	private DataInputStream dis;
	private DataOutputStream dos;
	public void setDis(DataInputStream dis) {
		//epic statement
		this.dis = dis;
	}	
	public void setDos(DataOutputStream dos) {
		//epic statement
		this.dos = dos;
	}	
	public DataInputStream getDis() {
		return dis;
	}
	public DataOutputStream getDos() {
		return dos;
	}
}

interface MyRunnable extends Runnable {
	public void kill();
}

class GraphicsClass implements MyRunnable {
	private HashMap<String,Panel> panels;
	private HashMap<String,BufferedImage> resources;
	private HashMap<String,TexturePaint> patterns;
	int shots;
	BufferStrategy bf;
	boolean run;
	SidePanels sp;
	GraphicsClass(BufferStrategy bf, HashMap<String,Panel> panels,HashMap<String,BufferedImage> resources,HashMap<String,TexturePaint> patterns, SidePanels sp) {
		this.panels = panels;
		this.resources = resources;
		this.patterns = patterns;
		this.bf = bf;
		this.run = true;
		this.sp = sp;
		this.shots = 0;
	}
	
	public void kill() {
		this.run = false;
	}
	
	public void run() {
		while(this.run) {
			try {
				Graphics2D g2d = (Graphics2D)bf.getDrawGraphics();
				//Use of antialiasing to have nicer lines
				g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
				g2d.setPaint(patterns.get("main_bg"));
				g2d.fill(new Rectangle(1920,1080));
				sp.run();
				for(Panel p : panels.values()) g2d.drawImage(p.img, new AffineTransform(1f,0f,0f,1f,p.x,p.y), null);
				g2d.drawImage(resources.get("overlay"), new AffineTransform(1f,0f,0f,1f,0,0), null);
				g2d.dispose();
				if(sp.takeShot()) {
					try {
						BufferedImage ss = new BufferedImage(1920,1080,BufferedImage.TYPE_INT_RGB);
						g2d = (Graphics2D)ss.getGraphics();
						g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
						g2d.setPaint(patterns.get("main_bg"));
						g2d.fill(new Rectangle(1920,1080));
						for(Panel p : panels.values()) g2d.drawImage(p.img, new AffineTransform(1f,0f,0f,1f,p.x,p.y), null);
						g2d.drawImage(resources.get("overlay"), new AffineTransform(1f,0f,0f,1f,0,0), null);
						g2d.dispose();
						File outfile = new File("screenshots/screenshot_"+shots+".png");
						outfile.createNewFile();
						shots++;
						ImageIO.write(ss,"png",outfile);
					} catch(Exception e) {
						System.out.println(e.getMessage());
					}
				}
				bf.show();
			} catch(Exception e) {
				;
			}
		}
	}
}

class Panel {
	int x;
	int y;
	int w;
	int h;
	BufferedImage img;
	
	Panel(int x, int y, int w, int h) {
		this.x = x;
		this.y = y;
		this.w = w;
		this.h = h;
		this.img = new BufferedImage(w+1,h+1,BufferedImage.TYPE_INT_ARGB);
	}
	Graphics getGraphics() {
		return img.getGraphics();
	}
	BufferedImage getImg() {
		return img;
	}
}

class Data {
	public int leftMotor;
	public int rightMotor;
	public int light;
	public byte color;
	public int sonar;
	public byte barcode;
	public byte paneel;
	public boolean takeShot;
	public boolean run;
	public int[] panicData;
	public boolean activatePanic;
	
	Data() {
		leftMotor = 0;
		rightMotor = 0;
		light = 0;
		color = 0;
		sonar = 0;
		barcode = 0;
		paneel = 0;
		takeShot = false;
		run = true;
		panicData = new int[36];
		activatePanic = false;
	}
}

class SidePanels /*implements MyRunnable*/ {
	HashMap<String,Panel> panels;
	HashMap<String,BufferedImage> r;
	HashMap<String,TexturePaint> p;
	Data d;
	BufferedImage wp = new BufferedImage(531,341,BufferedImage.TYPE_INT_ARGB);
	//boolean run;
	public SidePanels(HashMap<String,Panel> panels,HashMap<String,BufferedImage> r,HashMap<String,TexturePaint> p, Data d) {
		this.panels = panels;
		this.r = r;
		this.p = p;
		this.d = d;
		//this.run = true;
	}
	public void run() {
		/*while(run) {*/
			//motors
			Graphics2D g2d = this.preparePanel("Motors");
			g2d.drawString("L:"+ d.leftMotor,200,220);
			g2d.drawString("R:"+ d.rightMotor,290,220);
			g2d.draw(new Rectangle(200,90,30,100));
			g2d.draw(new Rectangle(290,90,30,100));
			g2d.drawLine(200,140,320,140);
			g2d.fill(new Rectangle(200,Math.min(140,140-d.leftMotor/9),30,Math.abs(d.leftMotor/9)));
			g2d.fill(new Rectangle(290,Math.min(140,140-d.rightMotor/9),30,Math.abs(d.rightMotor/9)));
			g2d.dispose();
			drawOnPanel("LT");
			//light interpretation
			g2d = this.preparePanel("Light Interpretation");
			g2d.drawString("Color:"+ LightColor.getColor(d.color).getName(),15,170);
			g2d.draw(new Rectangle(25,90,480,40));
			int bcode = Barcode.getBarcode(d.barcode).getCode();
			for(int i=1;i<=8;i++) {
				if((bcode&1)==1) g2d.fill(new Rectangle(25+480-i*60,90,60,40));
				bcode = bcode>>1;
			}
			g2d.drawString("Barcode:"+ Barcode.getBarcode(d.barcode).getName(),15,85);
			g2d.setStroke(new BasicStroke(2f));
			g2d.setColor(new Color(255,255,255));
			g2d.fill(new Rectangle(30,180,60,60));
			g2d.setColor(new Color(0,0,0));
			g2d.fill(new Rectangle(170,180,60,60));
			g2d.setColor(new Color(255,128,0));
			g2d.fill(new Rectangle(100,180,60,60));
			g2d.draw(new Rectangle(25+LightColor.getColor(d.color).getID()*70,175,70,70));
			g2d.dispose();
			drawOnPanel("RM");
			//sonar
			g2d = this.preparePanel("Sonar");
			g2d.drawString("Range:"+ d.sonar,15,85);
			g2d.draw(new Rectangle(25,90,480,40));
			g2d.fill(new Rectangle(25,90,d.sonar*2,40));
			g2d.dispose();
			drawOnPanel("RB");
			//light chart
			g2d = this.preparePanel("Light Value");
			g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BICUBIC);
			g2d.drawImage(r.get("icon_light"), new AffineTransform(1f,0f,0f,1f,10,95), null);
			g2d.drawString("Value:"+ d.light,50,300);
			g2d.dispose();
			drawOnPanel("RT");
		//}
	}
	
	/*public void kill() {
		this.run = false;
	}*/
		
	private Graphics2D preparePanel(String s) {
		Graphics2D g2d = (Graphics2D)wp.getGraphics();
		g2d.setBackground(new Color(0,0,0,0));
		g2d.clearRect(0,0,531,341);
		g2d.drawImage(r.get("side_bg"), new AffineTransform(), null);
		g2d.setColor(new Color(255,128,0));
		g2d.setFont(new Font("Monospaced",Font.BOLD,30));
		g2d.drawString(s,60,40);
		g2d.setFont(new Font("Monospaced",0,20));
		return g2d;
	}
	
	private void drawOnPanel(String p) {
		Panel panel = panels.get(p);
		Graphics2D g2d = (Graphics2D)panel.getGraphics();
		g2d.setBackground(new Color(0,0,0,0));
		g2d.clearRect(0,0,531,341);
		g2d.drawImage(wp, new AffineTransform(), null);
		g2d.dispose();
	}
	
	private void hidePanel(String p) {
		Panel panel = panels.get(p);
		Graphics2D g2d = (Graphics2D)panel.getGraphics();
		g2d.setBackground(new Color(0,0,0,0));
		g2d.clearRect(0,0,601,401);
		g2d.dispose();
	}
	
	boolean takeShot() {
		boolean t = d.takeShot;
		d.takeShot = false;
		return t;
	}
}

class LightChart implements MyRunnable {
	boolean run;
	Panel p;
	HashMap<String,BufferedImage> r;
	int prevLight;
	int curLight;
	Data d;
	public void kill() {
		this.run = false;
	}
	LightChart(Panel p, HashMap<String,BufferedImage> r, Data d) {
		this.r = r;
		this.p = p;
		this.d = d;
		this.prevLight = 0;
		this.curLight = 0;
		this.run = true;
	}
	
	public void run() {
		while(run) {
			Graphics2D g2d = (Graphics2D)p.getGraphics();
			g2d.setColor(new Color(255,128,0));
			g2d.drawImage(p.getImg(), new AffineTransform(1f,0f,0f,1f,-5,0), null);
			g2d.setBackground(new Color(0,0,0,255));
			g2d.clearRect(346,0,5,151);
			//g2d.draw(new Rectangle(0,0,350,150));
			curLight = d.light;
			g2d.drawLine(345,100-prevLight+25,350,100-curLight+25);
			g2d.drawLine(346,25,348,25);
			g2d.drawLine(346,125,348,125);
			prevLight = curLight;
			g2d.dispose();
			try {
				Thread.sleep(500);
			} catch(Exception e) {}
		}
	}	
}

enum Barcode {
	STRAIGHT ((byte)1,"Straight",0+0+0+0+8+2+4+1),
	LEFT ((byte)3,"Left",0+0+0+16+8+0+0+1),
	RIGHT ((byte)6,"Right",0+0+32+16+0+0+2+1),
	UP ((byte)4,"Up",0+0+0+16+0+4+2+0),
	DOWN ((byte)5,"Down",0+0+32+0+0+4+0+1),
	SEESAW ((byte)7,"Down",0+0+32+16+8+4+0+0),
	CONSTRICTION ((byte)8,"Down",0+64+0+0+0+0+2+1),
	INVALID ((byte)0,"Invalid",0);

	private final byte id;
	private final String name;
	private final int code;

	Barcode(byte id, String name, int code) {
        this.id = id;
        this.name = name;
        this.code = code;
    }
	

	public static Barcode getBarcode(byte id) {
		switch(id) {
			case 1: return STRAIGHT;
			case 3: return LEFT;
			case 6: return RIGHT;
			case 4: return UP;
			case 5: return DOWN;
			case 7: return SEESAW;
			case 8: return CONSTRICTION;
			default: return INVALID;
		}
	}
	
	public String getName() { return name; }
	public byte getID() { return id; }
	public int getCode() { return code; }
}

enum LightColor {
	WHITE ((byte)0,"White"),
	WOOD ((byte)1,"Wood"),
	BLACK ((byte)2,"Black"),
	INVALID ((byte)3,"Invalid");

	private final byte id;
	private final String name;

	LightColor(byte id, String name) {
        this.id = id;
        this.name = name;
    }
	
	public static LightColor getColor(byte id) {
		switch(id) {
			case 2: return BLACK;
			case 1: return WOOD;
			case 0: return WHITE;
			default: return INVALID;
		}
	}
	
	public byte getID() { return id; }
	public String getName() { return name; }
}

class Map {
	Panel p;
	HashMap<String,BufferedImage> r;
	ArrayList<Barcode> hist;
	int x;
	int y;
	int xdir;
	int ydir;
	int height;
	boolean loop;
	BufferedImage lp = new BufferedImage(531,701,BufferedImage.TYPE_INT_ARGB);
	Map(Panel p, HashMap<String,BufferedImage> r) {
		 this.p = p;
		 this.r = r;
		 this.height = 0;
		 x = 0;
		 y = 0;
		 xdir = 0;
		 ydir = -1;
		 hist = new ArrayList<Barcode>();
		 loop = false;
	}
	
	void update(Byte bt) {
		Barcode b = Barcode.getBarcode(bt);
		int prevx = x;
		int prevy = y;
		if(!loop) hist.add(b);
		int t;
		switch(b) {
			case LEFT:
				t = xdir;
				xdir = ydir;
				ydir = -t;
				break;
			case RIGHT:
				t = xdir;
				xdir = -ydir;
				ydir = t;
				break;
			case UP:
				height++;
				break;
			case DOWN:
				height--;
				break;
			default:;
		}
		x+=xdir;
		y+=ydir;
		if(x==0 && y==0 && height ==0) loop = true;
		
		//draw map
		Ellipse2D circ = new Ellipse2D.Double(15,100,500,500);
		Graphics2D g2d = this.prepareLargePanel();
		g2d.setStroke(new BasicStroke(2f));
		g2d.draw(circ);
		g2d.clip(circ);
		g2d.drawLine(265,350,265+xdir*15,350+ydir*15);
		g2d.setColor(new Color(0,0,0,64));
		g2d.fill(new Rectangle(0,0,531,701));
		int localx = 0;
		int localy = 0;
		int localxd = 0;
		int localyd = -1;
		for(Barcode bc : hist) {
			switch(bc) {
			case LEFT:
				g2d.drawImage(r.get("left"), new AffineTransform((float)-localyd,(float)localxd,(float)-localxd,(float)-localyd,(localx-prevx)*50+15+250-25+Math.max(0,50*localxd)+Math.max(0,50*localyd),(localy-prevy)*50+100+250-25-Math.min(0,50*localxd)+Math.max(0,50*localyd)), null);
				t = localxd;
				localxd = localyd;
				localyd = -t;
				localx+=localxd;
				localy+=localyd;
				break;
			case RIGHT:
				g2d.drawImage(r.get("right"), new AffineTransform((float)-localyd,(float)localxd,(float)-localxd,(float)-localyd,(localx-prevx)*50+15+250-25+Math.max(0,50*localxd)+Math.max(0,50*localyd),(localy-prevy)*50+100+250-25-Math.min(0,50*localxd)+Math.max(0,50*localyd)), null);
				t = localxd;
				localxd = -localyd;
				localyd = t;
				localx+=localxd;
				localy+=localyd;
				break;
			default:
				g2d.drawImage(r.get("straight"), new AffineTransform((float)-localyd,(float)localxd,(float)-localxd,(float)-localyd,(localx-prevx)*50+15+250-25+Math.max(0,50*localxd)+Math.max(0,50*localyd),(localy-prevy)*50+100+250-25-Math.min(0,50*localxd)+Math.max(0,50*localyd)), null);
				localx+=localxd;
				localy+=localyd;
			}
		}
		//g2d.drawImage(r.get("straight"), new AffineTransform(1f,0f,0f,1f,10,95), null);
		//g2d.drawImage(r.get("left"), new AffineTransform(1f,0f,0f,1f,100,95), null);
		//g2d.drawImage(r.get("right"), new AffineTransform(1f,0f,0f,1f,200,95), null);
		g2d.dispose();
		drawLarge();
	}
	private Graphics2D prepareLargePanel() {
		Graphics2D g2d = (Graphics2D)lp.getGraphics();
		g2d.setBackground(new Color(0,0,0,0));
		g2d.clearRect(0,0,531,701);
		//g2d.drawImage(r.get("side_bg"), new AffineTransform(), null);
		g2d.setColor(new Color(255,128,0));
		//g2d.setFont(new Font("Monospaced",Font.BOLD,30));
		//g2d.drawString(s,60,40);
		g2d.setFont(new Font("Monospaced",0,20));
		return g2d;
	}
	
	private void drawLarge() {
		Graphics2D g2d = (Graphics2D)p.getGraphics();
		g2d.setBackground(new Color(0,0,0,0));
		g2d.clearRect(0,0,531,701);
		g2d.drawImage(lp, new AffineTransform(), null);
		g2d.dispose();
	}
	
}

class Center {
	Panel p;
	HashMap<String,BufferedImage> r;
	BufferedImage lp = new BufferedImage(531,701,BufferedImage.TYPE_INT_ARGB);
	Center(Panel p, HashMap<String,BufferedImage> r) {
		 this.p = p;
		 this.r = r;
	}
	
	void update(Byte bt) {
		//draw map
		Graphics2D g2d = this.prepareLargePanel();
		g2d.setStroke(new BasicStroke(10f));
		Barcode b = Barcode.getBarcode(bt);
		switch(b) {
			case LEFT:
				g2d.drawLine(0,0,530,0);
				g2d.drawLine(530,0,530,530);
				g2d.drawString("Slope:",10,600);
				g2d.drawLine(200,600,300,600);
				break;
			case RIGHT:
				g2d.drawLine(0,0,530,0);
				g2d.drawLine(0,0,0,530);
				g2d.drawString("Slope:",10,600);
				g2d.drawLine(200,600,300,600);
				break;
			case UP:
				g2d.drawLine(0,0,530,0);
				g2d.drawLine(0,0,0,530);
				g2d.drawString("Slope:",10,600);
			case DOWN:
				g2d.drawLine(0,0,530,0);
				g2d.drawLine(0,0,0,530);
				g2d.drawString("Slope:",10,600);
				g2d.drawLine(200,600,300,650);
			case SEESAW:
				g2d.drawLine(0,0,530,0);
				g2d.drawLine(0,0,0,530);
				g2d.drawString("Slope:",10,600);
				g2d.drawLine(200,600,300,650);
				g2d.drawLine(200,600,300,550);
			case CONSTRICTION:
				g2d.drawLine(0,0,100,100);
				g2d.drawLine(530,0,430,100);
				g2d.drawLine(100,100,100,430);
				g2d.drawLine(430,100,430,430);
				g2d.drawLine(0,530,100,430);
				g2d.drawLine(530,530,430,430);
				g2d.drawLine(0,0,0,530);
				g2d.drawString("Slope:",10,600);
				g2d.drawLine(200,600,300,600);
				break;
			default:
				g2d.drawLine(0,0,0,530);
				g2d.drawLine(530,0,530,530);
				g2d.drawString("Slope:",10,600);
				g2d.drawLine(200,600,300,600);
		}
		//g2d.drawImage(r.get("straight"), new AffineTransform(1f,0f,0f,1f,10,95), null);
		//g2d.drawImage(r.get("left"), new AffineTransform(1f,0f,0f,1f,100,95), null);
		//g2d.drawImage(r.get("right"), new AffineTransform(1f,0f,0f,1f,200,95), null);
		g2d.dispose();
		drawLarge();
	}
	private Graphics2D prepareLargePanel() {
		Graphics2D g2d = (Graphics2D)lp.getGraphics();
		g2d.setBackground(new Color(0,0,0,0));
		g2d.clearRect(0,0,531,701);
		//g2d.drawImage(r.get("side_bg"), new AffineTransform(), null);
		g2d.setColor(new Color(255,128,0));
		//g2d.setFont(new Font("Monospaced",Font.BOLD,30));
		//g2d.drawString(s,60,40);
		g2d.setFont(new Font("Monospaced",0,20));
		return g2d;
	}
	
	private void drawLarge() {
		Graphics2D g2d = (Graphics2D)p.getGraphics();
		g2d.setBackground(new Color(0,0,0,0));
		g2d.clearRect(0,0,531,701);
		g2d.drawImage(lp, new AffineTransform(), null);
		g2d.dispose();
	}
	
}


class Panic {
	Panel p;
	HashMap<String,BufferedImage> r;
	Data d;
	Panic(Panel p, HashMap<String,BufferedImage> r, Data d) {
		 this.p = p;
		 this.r = r;
		 this.d = d;
	}
	void show() {
		Graphics2D g2d = (Graphics2D)p.getGraphics();
		g2d.setBackground(new Color(0,0,0,0));
		g2d.clearRect(0,0,p.w+1,p.h+1);
		g2d.drawImage(r.get("panic_bg"), new AffineTransform(), null);
		g2d.setColor(new Color(255,230,210));
		g2d.setFont(new Font("Monospaced",0,20));
		Ellipse2D circ = new Ellipse2D.Double(50,70,500,500);
		g2d.setBackground(new Color(255,128,0,64));
		g2d.draw(circ);
		g2d.clip(circ);
		g2d.clearRect(0,0,p.w+1,p.h+1);
		g2d.draw(circ);
		g2d.setBackground(new Color(0,0,0,0));
		double radcon = Math.PI/18d;
		int[] px = new int[36];
		int[] py = new int[36];
		for(int i=0;i<36;i++) {
			px[i] = (int)(300d - ((double)d.panicData[i])*Math.sin(radcon*(double)i));
			py[i] = (int)(300d - ((double)d.panicData[i])*Math.cos(radcon*(double)i));
		}
		Polygon poly = new Polygon(px,py,36);
		g2d.setColor(new Color(0,0,0,128));
		g2d.fill(poly);
		g2d.draw(poly);
		g2d.dispose();
		PanicDisposer pd = new PanicDisposer(p);
		Thread pd_thread = new Thread(pd);
		pd_thread.start();
	}
}

class PanicDisposer implements Runnable {
	Panel p;
	PanicDisposer(Panel p) {
		this.p = p;
	}
	public void run() {
		try {
			Thread.sleep(15000);
		} catch(Exception e) {;}
		Graphics2D g2d = (Graphics2D)p.getGraphics();
		g2d.setBackground(new Color(0,0,0,0));
		g2d.clearRect(0,0,p.w+1,p.h+1);
		g2d.dispose();
	}
}
